#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Word文档转Markdown工具
支持将.docx和.doc文件转换为.md格式
支持三种转换方式：内置解析器、Pandoc和格式转换
"""

import argparse
import os
import sys
import subprocess
import shutil
from pathlib import Path
import logging
from docx import Document
from docx.shared import Inches
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
import re
import tempfile


class Word2MarkdownConverter:
    """Word文档转Markdown转换器"""
    
    def __init__(self):
        self.setup_logging()
        self.check_pandoc_availability()
        self.check_doc_conversion_tools()
        
    def setup_logging(self):
        """设置日志"""
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.StreamHandler(sys.stdout)
            ]
        )
        self.logger = logging.getLogger(__name__)
    
    def check_pandoc_availability(self):
        """检查Pandoc是否可用"""
        self.pandoc_available = shutil.which("pandoc") is not None
        if self.pandoc_available:
            try:
                # 获取pandoc版本
                result = subprocess.run(['pandoc', '--version'], 
                                      capture_output=True, text=True, timeout=10)
                version_line = result.stdout.split('\n')[0]
                self.logger.info(f"检测到 {version_line}")
            except Exception as e:
                self.logger.warning(f"Pandoc可用但版本检测失败: {e}")
        else:
            self.logger.info("未检测到Pandoc，将使用内置转换器")
    
    def check_doc_conversion_tools(self):
        """检查.doc转换工具的可用性"""
        self.doc_tools = {}
        
        # 检查LibreOffice
        libreoffice_cmd = None
        for cmd in ['libreoffice', 'soffice']:
            if shutil.which(cmd):
                libreoffice_cmd = cmd
                break
        
        if libreoffice_cmd:
            self.doc_tools['libreoffice'] = libreoffice_cmd
            self.logger.info(f"检测到LibreOffice: {libreoffice_cmd}")
        
        # 检查antiword (Unix/Linux)
        if shutil.which("antiword"):
            self.doc_tools['antiword'] = "antiword"
            self.logger.info("检测到antiword")
        
        # 检查catdoc (Unix/Linux)
        if shutil.which("catdoc"):
            self.doc_tools['catdoc'] = "catdoc"
            self.logger.info("检测到catdoc")
        
        # 尝试导入doc2docx (Windows/macOS with MS Word)
        try:
            import doc2docx
            self.doc_tools['doc2docx'] = doc2docx
            self.logger.info("检测到doc2docx库")
        except ImportError:
            pass
        
        # 尝试导入docx2txt
        try:
            import docx2txt
            self.doc_tools['docx2txt'] = docx2txt
            self.logger.info("检测到docx2txt库")
        except ImportError:
            pass
    
    def detect_file_format(self, file_path):
        """检测文件的真实格式"""
        try:
            # 尝试读取文件头部分来检测格式
            with open(file_path, 'rb') as f:
                header = f.read(8)
            
            # .docx文件是ZIP格式，以PK开头
            if header.startswith(b'PK'):
                return 'docx'
            # .doc文件是OLE格式，以特定字节序列开头
            elif header.startswith(b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1'):
                return 'doc'
            else:
                # 基于扩展名猜测
                ext = Path(file_path).suffix.lower()
                if ext == '.docx':
                    return 'docx'
                elif ext == '.doc':
                    return 'doc'
                else:
                    return 'unknown'
        except Exception as e:
            self.logger.warning(f"无法检测文件格式: {e}")
            # 回退到扩展名检测
            ext = Path(file_path).suffix.lower()
            return 'docx' if ext == '.docx' else 'doc' if ext == '.doc' else 'unknown'
    
    def convert_doc_to_docx(self, doc_path):
        """将.doc文件转换为.docx格式"""
        temp_docx = None
        
        try:
            # 创建临时文件
            temp_dir = tempfile.mkdtemp()
            temp_docx = os.path.join(temp_dir, f"{Path(doc_path).stem}.docx")
            
            # 方法1: 使用LibreOffice转换
            if 'libreoffice' in self.doc_tools:
                self.logger.info("使用LibreOffice转换.doc到.docx")
                cmd = [
                    self.doc_tools['libreoffice'],
                    '--headless',
                    '--convert-to', 'docx',
                    '--outdir', temp_dir,
                    doc_path
                ]
                
                result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
                if result.returncode == 0 and os.path.exists(temp_docx):
                    self.logger.info("LibreOffice转换成功")
                    return temp_docx
                else:
                    self.logger.warning(f"LibreOffice转换失败: {result.stderr}")
            
            # 方法2: 使用doc2docx库 (需要MS Word)
            if 'doc2docx' in self.doc_tools:
                self.logger.info("使用doc2docx转换.doc到.docx")
                try:
                    self.doc_tools['doc2docx'].convert(doc_path, temp_docx)
                    if os.path.exists(temp_docx):
                        self.logger.info("doc2docx转换成功")
                        return temp_docx
                except Exception as e:
                    self.logger.warning(f"doc2docx转换失败: {e}")
            
            # 如果转换失败，返回None
            return None
            
        except Exception as e:
            self.logger.error(f"转换.doc到.docx失败: {e}")
            if temp_docx and os.path.exists(temp_docx):
                try:
                    os.remove(temp_docx)
                except:
                    pass
            return None
    
    def extract_text_from_doc(self, doc_path):
        """从.doc文件提取纯文本"""
        text = None
        
        # 方法1: 使用antiword
        if 'antiword' in self.doc_tools:
            try:
                self.logger.info("使用antiword提取文本")
                result = subprocess.run([self.doc_tools['antiword'], doc_path], 
                                      capture_output=True, text=True, timeout=30)
                if result.returncode == 0:
                    text = result.stdout
                    self.logger.info(f"antiword提取成功 ({len(text)} 字符)")
                    return text
            except Exception as e:
                self.logger.warning(f"antiword提取失败: {e}")
        
        # 方法2: 使用catdoc
        if 'catdoc' in self.doc_tools:
            try:
                self.logger.info("使用catdoc提取文本")
                result = subprocess.run([self.doc_tools['catdoc'], doc_path], 
                                      capture_output=True, text=True, timeout=30)
                if result.returncode == 0:
                    text = result.stdout
                    self.logger.info(f"catdoc提取成功 ({len(text)} 字符)")
                    return text
            except Exception as e:
                self.logger.warning(f"catdoc提取失败: {e}")
        
        # 方法3: 使用docx2txt库
        if 'docx2txt' in self.doc_tools:
            try:
                self.logger.info("使用docx2txt提取文本")
                text = self.doc_tools['docx2txt'].process(doc_path)
                if text and text.strip():
                    self.logger.info(f"docx2txt提取成功 ({len(text)} 字符)")
                    return text
            except Exception as e:
                self.logger.warning(f"docx2txt提取失败: {e}")
        
        return None
    
    def convert_text_to_markdown(self, text):
        """将纯文本转换为基础Markdown格式"""
        if not text:
            return ""
        
        lines = text.split('\n')
        markdown_lines = []
        
        for line in lines:
            line = line.strip()
            if not line:
                markdown_lines.append("")
                continue
            
            # 简单的标题检测
            if self._is_likely_heading(line):
                # 根据内容特征判断标题级别
                if any(keyword in line.lower() for keyword in ['第一章', '第二章', '第三章', '第四章', '第五章', '第六章']):
                    markdown_lines.append(f"# {line}")
                elif any(keyword in line.lower() for keyword in ['第一节', '第二节', '第三节', '一、', '二、', '三、']):
                    markdown_lines.append(f"## {line}")
                elif line.isupper() or len(line) < 30:
                    markdown_lines.append(f"### {line}")
                else:
                    markdown_lines.append(f"## {line}")
            else:
                markdown_lines.append(line)
            
            markdown_lines.append("")
        
        return '\n'.join(markdown_lines)
    
    def _is_likely_heading(self, line):
        """判断一行文本是否可能是标题"""
        if len(line) > 100:  # 太长的行不太可能是标题
            return False
        
        # 标题特征
        heading_indicators = [
            '第', '章', '节', '部分', '段', '条',
            '一、', '二、', '三、', '四、', '五、',
            '1.', '2.', '3.', '4.', '5.',
            'title', 'chapter', 'section', 'part'
        ]
        
        line_lower = line.lower()
        return (
            line.isupper() or  # 全大写
            any(indicator in line_lower for indicator in heading_indicators) or
            (len(line) < 50 and not line.endswith('.') and not line.endswith('。'))  # 短行且不以句号结尾
        )
    
    def convert_document_to_markdown(self, doc_path, output_path=None, use_pandoc=None):
        """
        将Word文档转换为Markdown格式 - 支持.doc和.docx
        
        Args:
            doc_path (str): Word文档路径 (.doc 或 .docx)
            output_path (str): 输出的Markdown文件路径，可选
            use_pandoc (bool): 是否使用Pandoc，None表示自动选择
            
        Returns:
            str: 输出文件路径
        """
        try:
            # 检查输入文件
            if not os.path.exists(doc_path):
                raise FileNotFoundError(f"文件不存在: {doc_path}")
            
            # 检测文件格式
            file_format = self.detect_file_format(doc_path)
            self.logger.info(f"检测到文件格式: {file_format}")
            
            if file_format not in ['doc', 'docx']:
                raise ValueError(f"不支持的文件格式: {file_format}，仅支持.doc和.docx格式")
            
            # 设置输出路径
            if output_path is None:
                base_name = Path(doc_path).stem
                output_path = f"{base_name}.md"
            
            # 决定使用哪种转换方式
            if use_pandoc is None:
                use_pandoc = self.pandoc_available
            elif use_pandoc and not self.pandoc_available:
                self.logger.warning("要求使用Pandoc但未检测到，将使用内置转换器")
                use_pandoc = False
            
            # 处理.docx文件
            if file_format == 'docx':
                if use_pandoc:
                    return self._convert_with_pandoc(doc_path, output_path)
                else:
                    return self._convert_with_builtin(doc_path, output_path)
            
            # 处理.doc文件
            elif file_format == 'doc':
                return self._convert_doc_file(doc_path, output_path, use_pandoc)
                
        except Exception as e:
            self.logger.error(f"转换失败: {str(e)}")
            raise
    
    def _convert_doc_file(self, doc_path, output_path, use_pandoc):
        """处理.doc文件的转换"""
        temp_docx = None
        
        try:
            # 策略1: 如果有Pandoc，直接尝试转换.doc
            if use_pandoc and self.pandoc_available:
                self.logger.info("尝试使用Pandoc直接转换.doc文件")
                try:
                    return self._convert_with_pandoc(doc_path, output_path)
                except Exception as e:
                    self.logger.warning(f"Pandoc直接转换.doc失败: {e}")
            
            # 策略2: 转换.doc到.docx，然后转换
            temp_docx = self.convert_doc_to_docx(doc_path)
            if temp_docx:
                self.logger.info("成功转换.doc到.docx，继续转换为Markdown")
                try:
                    if use_pandoc:
                        result = self._convert_with_pandoc(temp_docx, output_path)
                    else:
                        result = self._convert_with_builtin(temp_docx, output_path)
                    return result
                finally:
                    # 清理临时文件
                    try:
                        os.remove(temp_docx)
                        os.rmdir(os.path.dirname(temp_docx))
                    except:
                        pass
            
            # 策略3: 提取纯文本并转换为Markdown
            self.logger.info("尝试从.doc文件提取纯文本")
            text = self.extract_text_from_doc(doc_path)
            if text:
                markdown_content = self.convert_text_to_markdown(text)
                with open(output_path, 'w', encoding='utf-8') as f:
                    f.write(markdown_content)
                self.logger.info(f"文本提取转换完成: {output_path}")
                return output_path
            
            # 所有方法都失败
            raise Exception("无法转换.doc文件，请尝试以下解决方案：\n"
                          "1. 安装LibreOffice或MS Word\n"
                          "2. 手动将.doc文件另存为.docx格式\n"
                          "3. 使用在线转换工具转换格式")
            
        except Exception as e:
            if temp_docx and os.path.exists(temp_docx):
                try:
                    os.remove(temp_docx)
                    os.rmdir(os.path.dirname(temp_docx))
                except:
                    pass
            raise
    
    # 保持原有的方法名兼容性
    def convert_docx_to_markdown(self, docx_path, output_path=None, use_pandoc=None):
        """兼容性方法，现在支持.doc和.docx"""
        return self.convert_document_to_markdown(docx_path, output_path, use_pandoc)
    
    def _convert_with_pandoc(self, docx_path, output_path):
        """使用Pandoc进行转换"""
        self.logger.info(f"使用Pandoc转换: {docx_path} -> {output_path}")
        
        try:
            # 构建pandoc命令
            cmd = [
                'pandoc',
                docx_path,
                '-o', output_path,
                '--from', 'docx',
                '--to', 'markdown',
                '--extract-media=./images',  # 提取图片到images目录
                '--wrap=auto',  # 自动换行
                '--markdown-headings=atx',  # 使用ATX风格的标题（#）
            ]
            
            # 执行转换
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
            
            if result.returncode != 0:
                raise Exception(f"Pandoc转换失败: {result.stderr}")
            
            self.logger.info(f"Pandoc转换完成: {output_path}")
            return output_path
            
        except subprocess.TimeoutExpired:
            raise Exception("Pandoc转换超时")
        except Exception as e:
            self.logger.error(f"Pandoc转换错误: {str(e)}")
            raise
    
    def _convert_with_builtin(self, docx_path, output_path):
        """使用内置转换器进行转换"""
        self.logger.info(f"使用内置转换器: {docx_path} -> {output_path}")
        
        # 读取Word文档
        doc = Document(docx_path)
        
        # 转换内容
        markdown_content = self._convert_document_to_markdown(doc)
        
        # 写入Markdown文件
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(markdown_content)
        
        self.logger.info(f"内置转换器转换完成: {output_path}")
        return output_path
    
    def _convert_document_to_markdown(self, doc):
        """将Word文档对象转换为Markdown内容"""
        markdown_lines = []
        
        for paragraph in doc.paragraphs:
            # 跳过空段落
            if not paragraph.text.strip():
                markdown_lines.append("")
                continue
            
            # 处理标题
            if paragraph.style.name.startswith('Heading'):
                level = self._get_heading_level(paragraph.style.name)
                markdown_lines.append(f"{'#' * level} {paragraph.text}")
                markdown_lines.append("")
            else:
                # 处理普通段落
                formatted_text = self._format_paragraph_text(paragraph)
                if formatted_text:
                    markdown_lines.append(formatted_text)
                    markdown_lines.append("")
        
        # 处理表格
        for table in doc.tables:
            table_md = self._convert_table_to_markdown(table)
            if table_md:
                markdown_lines.extend(table_md)
                markdown_lines.append("")
        
        return '\n'.join(markdown_lines)
    
    def _get_heading_level(self, style_name):
        """获取标题级别"""
        if 'Heading 1' in style_name:
            return 1
        elif 'Heading 2' in style_name:
            return 2
        elif 'Heading 3' in style_name:
            return 3
        elif 'Heading 4' in style_name:
            return 4
        elif 'Heading 5' in style_name:
            return 5
        elif 'Heading 6' in style_name:
            return 6
        else:
            return 1
    
    def _format_paragraph_text(self, paragraph):
        """格式化段落文本，处理加粗、斜体等格式"""
        formatted_text = ""
        
        for run in paragraph.runs:
            text = run.text
            
            # 处理加粗
            if run.bold:
                text = f"**{text}**"
            
            # 处理斜体
            if run.italic:
                text = f"*{text}*"
            
            # 处理下划线（转为加粗）
            if run.underline:
                text = f"**{text}**"
            
            formatted_text += text
        
        # 处理段落对齐（转为引用）
        if paragraph.alignment == WD_PARAGRAPH_ALIGNMENT.CENTER:
            formatted_text = f"> {formatted_text}"
        
        return formatted_text.strip()
    
    def _convert_table_to_markdown(self, table):
        """将Word表格转换为Markdown表格"""
        if not table.rows:
            return []
        
        markdown_table = []
        
        # 处理表头
        header_row = table.rows[0]
        headers = [cell.text.strip() for cell in header_row.cells]
        markdown_table.append("| " + " | ".join(headers) + " |")
        markdown_table.append("| " + " | ".join(["---"] * len(headers)) + " |")
        
        # 处理数据行
        for row in table.rows[1:]:
            cells = [cell.text.strip() for cell in row.cells]
            markdown_table.append("| " + " | ".join(cells) + " |")
        
        return markdown_table
    
    def batch_convert(self, input_dir, output_dir=None, use_pandoc=None):
        """
        批量转换目录下的所有Word文档
        
        Args:
            input_dir (str): 输入目录
            output_dir (str): 输出目录，可选
            use_pandoc (bool): 是否使用Pandoc
        """
        input_path = Path(input_dir)
        
        if not input_path.exists():
            raise FileNotFoundError(f"输入目录不存在: {input_dir}")
        
        if output_dir is None:
            output_dir = input_dir
        
        output_path = Path(output_dir)
        output_path.mkdir(exist_ok=True)
        
        # 查找所有Word文档 (.doc 和 .docx)
        doc_files = list(input_path.glob("*.docx")) + list(input_path.glob("*.doc"))
        
        if not doc_files:
            self.logger.warning(f"在目录 {input_dir} 中未找到Word文档")
            return
        
        converter_name = "Pandoc" if (use_pandoc and self.pandoc_available) else "内置转换器"
        self.logger.info(f"找到 {len(doc_files)} 个文件待转换（使用{converter_name}）")
        
        success_count = 0
        for doc_file in doc_files:
            try:
                output_file = output_path / f"{doc_file.stem}.md"
                self.convert_document_to_markdown(str(doc_file), str(output_file), use_pandoc)
                success_count += 1
            except Exception as e:
                self.logger.error(f"转换文件 {doc_file} 失败: {str(e)}")
        
        self.logger.info(f"批量转换完成，成功转换 {success_count}/{len(doc_files)} 个文件")
    
    def get_converter_info(self):
        """获取转换器信息"""
        info = {
            'builtin_available': True,
            'pandoc_available': self.pandoc_available,
            'doc_tools': list(self.doc_tools.keys()),
            'supported_formats': ['docx', 'doc'],
            'recommended': 'pandoc' if self.pandoc_available else 'builtin'
        }
        return info


def install_pandoc_guide():
    """显示Pandoc安装指南"""
    guide = """
🔧 Pandoc安装指南

Pandoc是一个强大的文档转换工具，强烈推荐安装以获得最佳转换效果。

Windows安装：
  方法1：从官网下载安装包
    https://pandoc.org/installing.html
  
  方法2：使用包管理器
    choco install pandoc    (Chocolatey)
    scoop install pandoc    (Scoop)

macOS安装：
  brew install pandoc

Linux安装：
  Ubuntu/Debian: sudo apt-get install pandoc
  CentOS/RHEL:   sudo yum install pandoc

安装完成后重新运行程序即可自动检测。

💡 .doc文件支持工具安装：

LibreOffice (推荐，免费)：
  Windows: https://www.libreoffice.org/download/download/
  macOS:   brew install --cask libreoffice
  Linux:   sudo apt-get install libreoffice

其他工具：
  antiword (Linux/macOS): sudo apt-get install antiword
  catdoc (Linux/macOS):   sudo apt-get install catdoc
  doc2docx (需MS Word):   pip install doc2docx
"""
    print(guide)


def main():
    """主函数"""
    parser = argparse.ArgumentParser(description="Word文档转Markdown工具 - 支持.doc和.docx格式")
    parser.add_argument("input", help="输入的Word文档路径或目录 (.doc或.docx)")
    parser.add_argument("-o", "--output", help="输出文件或目录路径")
    parser.add_argument("-b", "--batch", action="store_true", help="批量转换模式")
    parser.add_argument("--engine", choices=['auto', 'pandoc', 'builtin'], 
                       default='auto', help="转换引擎选择")
    parser.add_argument("--install-guide", action="store_true", 
                       help="显示Pandoc和.doc支持工具安装指南")
    
    args = parser.parse_args()
    
    if args.install_guide:
        install_pandoc_guide()
        return
    
    converter = Word2MarkdownConverter()
    
    # 显示可用的转换器信息
    info = converter.get_converter_info()
    print(f"支持格式: {', '.join(info['supported_formats'])}")
    print(f"可用转换器: 内置转换器✅, Pandoc{'✅' if info['pandoc_available'] else '❌'}")
    if info['doc_tools']:
        print(f".doc支持工具: {', '.join(info['doc_tools'])}")
    
    # 确定使用哪个引擎
    use_pandoc = None
    if args.engine == 'pandoc':
        use_pandoc = True
    elif args.engine == 'builtin':
        use_pandoc = False
    # auto模式让转换器自动选择
    
    try:
        if args.batch:
            # 批量转换模式
            converter.batch_convert(args.input, args.output, use_pandoc)
        else:
            # 单文件转换模式
            output_file = converter.convert_document_to_markdown(args.input, args.output, use_pandoc)
            print(f"转换完成: {output_file}")
    
    except Exception as e:
        print(f"错误: {str(e)}")
        if "pandoc" in str(e).lower() and not info['pandoc_available']:
            print("\n💡 提示: 运行 'python word2md.py --install-guide' 查看Pandoc安装指南")
        elif ".doc" in str(e).lower() and not info['doc_tools']:
            print("\n💡 提示: 运行 'python word2md.py --install-guide' 查看.doc支持工具安装指南")
        sys.exit(1)


if __name__ == "__main__":
    main() 